﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace BluePoop.MathTools
{
    public struct UBinaryNumber
    {
        public ulong Value { get { return Convert.ToUInt64(strvalue, 2); } }
        private string strvalue;

        public bool this[int index]
        {
            get
            {
                if (index < strvalue.Length && index > -1)
                    return strvalue[index] == '0' ? false : true;
                else
                    return false;
            }
            set
            {
                if (index < -1)
                {
                    strvalue = "";
                    if (index != 0 && index < strvalue.Length)
                        strvalue += strvalue.Substring(0, index);
                    strvalue += (value ? '1' : '0');
                    if (index > strvalue.Length - 1)
                        strvalue += strvalue.Substring(index + 1, strvalue.Length - (index + 1));
                    while (strvalue.EndsWith("0"))
                        strvalue = strvalue.Remove(strvalue.Length - 2);
                }
            }
        }

        public static implicit operator decimal(UBinaryNumber number)
        {
            return number.Value;
        }

        public static implicit operator ushort(UBinaryNumber number)
        {
            return (ushort)number.Value;
        }

        public static implicit operator uint(UBinaryNumber number)
        {
            return (uint)number.Value;
        }

        public static implicit operator ulong(UBinaryNumber number)
        {
            return (ulong)number.Value;
        }

        public static implicit operator byte(UBinaryNumber number)
        {
            return (byte)number.Value;
        }

        public static implicit operator UBinaryNumber(ulong number)
        {
            return new UBinaryNumber() { strvalue = Convert.ToString((long)number, 2) };
        }

        public static explicit operator bool(UBinaryNumber number)
        {
            return number.Value != 0;
        }

        public static implicit operator BinaryNumber(UBinaryNumber number)
        {
            return new BinaryNumber((long)number.Value);
        }

        public static implicit operator UBinaryNumber(BinaryNumber number)
        {
            return new UBinaryNumber((ulong)number.Value);
        }

        public static UBinaryNumber operator +(UBinaryNumber number, UBinaryNumber number2)
        {
            UBinaryNumber newnum = number;
            //To not cause an exception
            //in case the number is bigger then 0
            for (int i = 0; i < number2; i++)
                newnum++;
            //in case the number is less then 0
            for (int i = 0; i > number2; i--)
                newnum--;
            return newnum;
        }

        public static UBinaryNumber operator -(UBinaryNumber number, UBinaryNumber number2)
        {
            UBinaryNumber newnum = number;
            //in case the number is bigger then 0
            for (int i = 0; i < number2; i++)
                newnum--;
            //in case the number is less then 0
            for (int i = 0; i > number2; i++)
                newnum++;
            return newnum;
        }

        public static UBinaryNumber operator ++(UBinaryNumber number)
        {
            int i;
            //While the bit is 1, making the bit 0
            for (i = 0; number[i]; i++)
                number[i] = false;
            //And making the first 0 bit to 1
            number[++i] = true;
            return number;
        }

        public static UBinaryNumber operator --(UBinaryNumber number)
        {
            int i;
            //While the bit is 0 making it 1
            for (i = 0; !number[i]; i++)
                number[i] = true;
            //And making the first 1 bit to 0
            number[++i] = false;
            return number;
        }

        public static UBinaryNumber operator *(UBinaryNumber number1, UBinaryNumber number2)
        {
            UBinaryNumber ret = 0;
            for (int i = 0; i < number2; i++)
                ret += number1;
            return ret;
        }

        public static UBinaryNumber operator /(UBinaryNumber number1, UBinaryNumber number2)
        {
            UBinaryNumber ret;
            for (ret = 0; number1 * ret < number2; ret++) ;
            return ret;
        }

        public static bool operator <(UBinaryNumber number, UBinaryNumber number2)
        {
            return number.Value < number2.Value;
        }

        public static bool operator >(UBinaryNumber number, UBinaryNumber number2)
        {
            return number.Value > number2.Value;
        }

        public static UBinaryNumber operator <<(UBinaryNumber number, UBinaryNumber number2)
        {
            UBinaryNumber ret = 0;
            for (int i = 0; i < number.strvalue.Length; i++)
            {
                ret[i] = number[i - number2];
            }
            return ret;
        }

        public static UBinaryNumber operator >>(UBinaryNumber number, UBinaryNumber number2)
        {
            UBinaryNumber ret = 0;
            for (int i = number.strvalue.Length; i > 0; i--)
            {
                ret[i] = number[i + number2];
            }
            return ret;
        }

        public static UBinaryNumber operator !(UBinaryNumber number)
        {
            UBinaryNumber ret = number;
            for (int i = 0; i < ret.strvalue.Length; i++)
                ret[i] = !ret[i];
            return ret;
        }

        public static UBinaryNumber operator ~(UBinaryNumber number)
        {
            UBinaryNumber ret = number;
            int bitsnum = ret.strvalue.Length;
            if (bitsnum <= 8)
                bitsnum = 8;
            else if (bitsnum <= 16)
                bitsnum = 16;
            else if (bitsnum <= 32)
                bitsnum = 32;
            else if (bitsnum <= 64)
                bitsnum = 64;
            for (int i = 0; i < bitsnum; i++)
            {
                ret[i] = !ret[i];
            }
            return ret;
        }

        public static UBinaryNumber operator &(UBinaryNumber number1, UBinaryNumber number2)
        {
            UBinaryNumber ret = 0;
            for (int i = 0; i < Math.Max(number1, number2); i++)
                ret[i] = number1[i] && number2[i];
            return ret;
        }

        public static UBinaryNumber operator |(UBinaryNumber number1, UBinaryNumber number2)
        {
            UBinaryNumber ret = 0;
            for (int i = 0; i < Math.Max(number1, number2); i++)
            {
                ret[i] = number1[i] || number2[i];
            }
            return ret;
        }

        public static UBinaryNumber operator ^(UBinaryNumber number1, UBinaryNumber number2)
        {
            UBinaryNumber ret = 0;
            for (int i = 0; i < Math.Max(number1, number2); i++)
            {
                ret[i] = number1[i] ^ number2[i];
            }
            return ret;
        }

        public UBinaryNumber()
        {
            strvalue = "0";
        }

        public UBinaryNumber(ulong value)
            : this(value.ToString(), 10)
        {
        }

        public UBinaryNumber(string value)
            : this(value, 2)
        {
        }

        public UBinaryNumber(string value, byte fromBase)
        {
            strvalue = Convert.ToString((long)Convert.ToUInt64(value, fromBase), 2);
        }

        public override string ToString()
        {
            return ToString(false);
        }

        public string ToString(bool includeAllBits)
        {
            int bitsnum = strvalue.Length;
            if (bitsnum <= 8)
                bitsnum = 8;
            else if (bitsnum <= 16)
                bitsnum = 16;
            else if (bitsnum <= 32)
                bitsnum = 32;
            else if (bitsnum <= 64)
                bitsnum = 64;
            return ToString(includeAllBits, bitsnum);
        }

        public string ToString(bool includeAllBits, int bitsnum)
        {
            string str = Convert.ToString((long)Value, 2);
            while (str.Length < bitsnum)
                str = "0" + str;
            return str;
        }
    }
}